/*
Copyright 2010 GHI Electronics LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 
*/

using System;
using Microsoft.SPOT;

namespace GHIElectronics.Graphics.Simple128x64
{
    class GPainter
    {

        static byte[] gylph_reg = new byte[129 * 5]
		{
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			0x00,0x00,0x00,0x00,0x00,
			
			0x00,0x00,0x00,0x00,0x00, /* Espace	0x20 */
			0x00,0x00,0x4f,0x00,0x00, /* ! */
			0x00,0x07,0x00,0x07,0x00, /* " */
			0x14,0x7f,0x14,0x7f,0x14, /* # */
			0x24,0x2a,0x7f,0x2a,0x12, /* 0x */
			0x23,0x13,0x08,0x64,0x62, /* % */
			0x36,0x49,0x55,0x22,0x20, /* & */
			0x00,0x05,0x03,0x00,0x00, /* ' */
			0x00,0x1c,0x22,0x41,0x00, /* ( */
			0x00,0x41,0x22,0x1c,0x00, /* ) */
			0x14,0x08,0x3e,0x08,0x14, /* // */
			0x08,0x08,0x3e,0x08,0x08, /* + */
			0x50,0x30,0x00,0x00,0x00, /* , */
			0x08,0x08,0x08,0x08,0x08, /* - */ 
			0x00,0x60,0x60,0x00,0x00, /* . */
			0x20,0x10,0x08,0x04,0x02, /* / */
			0x3e,0x51,0x49,0x45,0x3e, /* 0		0x30 */
			0x00,0x42,0x7f,0x40,0x00, /* 1 */
			0x42,0x61,0x51,0x49,0x46, /* 2 */
			0x21,0x41,0x45,0x4b,0x31, /* 3 */
			0x18,0x14,0x12,0x7f,0x10, /* 4 */
			0x27,0x45,0x45,0x45,0x39, /* 5 */
			0x3c,0x4a,0x49,0x49,0x30, /* 6 */
			0x01,0x71,0x09,0x05,0x03, /* 7 */
			0x36,0x49,0x49,0x49,0x36, /* 8 */
			0x06,0x49,0x49,0x29,0x1e, /* 9 */
			0x00,0x36,0x36,0x00,0x00, /* : */
			0x00,0x56,0x36,0x00,0x00, /* ; */
			0x08,0x14,0x22,0x41,0x00, /* < */
			0x14,0x14,0x14,0x14,0x14, /* = */
			0x00,0x41,0x22,0x14,0x08, /* > */
			0x02,0x01,0x51,0x09,0x06, /* ? */
			0x3e,0x41,0x5d,0x55,0x1e, /* @		0x40 */
			0x7e,0x11,0x11,0x11,0x7e, /* A */
			0x7f,0x49,0x49,0x49,0x36, /* B */
			0x3e,0x41,0x41,0x41,0x22, /* C */
			0x7f,0x41,0x41,0x22,0x1c, /* D */
			0x7f,0x49,0x49,0x49,0x41, /* E */
			0x7f,0x09,0x09,0x09,0x01, /* F */
			0x3e,0x41,0x49,0x49,0x7a, /* G */
			0x7f,0x08,0x08,0x08,0x7f, /* H */
			0x00,0x41,0x7f,0x41,0x00, /* I */
			0x20,0x40,0x41,0x3f,0x01, /* J */
			0x7f,0x08,0x14,0x22,0x41, /* K */
			0x7f,0x40,0x40,0x40,0x40, /* L */
			0x7f,0x02,0x0c,0x02,0x7f, /* M */
			0x7f,0x04,0x08,0x10,0x7f, /* N */
			0x3e,0x41,0x41,0x41,0x3e, /* O */
			0x7f,0x09,0x09,0x09,0x06, /* P		0x50 */
			0x3e,0x41,0x51,0x21,0x5e, /* Q */
			0x7f,0x09,0x19,0x29,0x46, /* R */
			0x26,0x49,0x49,0x49,0x32, /* S */
			0x01,0x01,0x7f,0x01,0x01, /* T */
			0x3f,0x40,0x40,0x40,0x3f, /* U */
			0x1f,0x20,0x40,0x20,0x1f, /* V */
			0x3f,0x40,0x38,0x40,0x3f, /* W */
			0x63,0x14,0x08,0x14,0x63, /* X */
			0x07,0x08,0x70,0x08,0x07, /* Y */
			0x61,0x51,0x49,0x45,0x43, /* Z */
			0x00,0x7f,0x41,0x41,0x00, /* [ */
			0x02,0x04,0x08,0x10,0x20, /* \ */ 
			0x00,0x41,0x41,0x7f,0x00, /* ] */
			0x04,0x02,0x01,0x02,0x04, /* ^ */
			0x40,0x40,0x40,0x40,0x40, /* _ */
			0x00,0x00,0x03,0x05,0x00, /* `		0x60 */
			0x20,0x54,0x54,0x54,0x78, /* a */
			0x7F,0x44,0x44,0x44,0x38, /* b */
			0x38,0x44,0x44,0x44,0x44, /* c */
			0x38,0x44,0x44,0x44,0x7f, /* d */
			0x38,0x54,0x54,0x54,0x18, /* e */
			0x04,0x04,0x7e,0x05,0x05, /* f */
			0x08,0x54,0x54,0x54,0x3c, /* g */
			0x7f,0x08,0x04,0x04,0x78, /* h */
			0x00,0x44,0x7d,0x40,0x00, /* i */
			0x20,0x40,0x44,0x3d,0x00, /* j */
			0x7f,0x10,0x28,0x44,0x00, /* k */
			0x00,0x41,0x7f,0x40,0x00, /* l */
			0x7c,0x04,0x7c,0x04,0x78, /* m */
			0x7c,0x08,0x04,0x04,0x78, /* n */
			0x38,0x44,0x44,0x44,0x38, /* o */
			0x7c,0x14,0x14,0x14,0x08, /* p		0x70 */
			0x08,0x14,0x14,0x14,0x7c, /* q */
			0x7c,0x08,0x04,0x04,0x00, /* r */
			0x48,0x54,0x54,0x54,0x24, /* s */
			0x04,0x04,0x3f,0x44,0x44, /* t */
			0x3c,0x40,0x40,0x20,0x7c, /* u */
			0x1c,0x20,0x40,0x20,0x1c, /* v */
			0x3c,0x40,0x30,0x40,0x3c, /* w */
			0x44,0x28,0x10,0x28,0x44, /* x */
			0x0c,0x50,0x50,0x50,0x3c, /* y */
			0x44,0x64,0x54,0x4c,0x44, /* z */
			0x08,0x36,0x41,0x41,0x00, /* { */
			0x00,0x00,0x77,0x00,0x00, /* | */
			0x00,0x41,0x41,0x36,0x08, /* } */
			0x08,0x08,0x2a,0x1c,0x08, /* <- */
			0x08,0x1c,0x2a,0x08,0x08, /* -> */
			0xff,0xff,0xff,0xff,0xff, /* 	 	0x80 */
        };
        static byte[] character_small = new byte[]
		{
			0x00,0x00,0x00, /* Espace	0x20 */         
			0x00,0x5C,0x00, /* ! */                         
			0x0C,0x00,0x0C, /* " */                         
			0x7C,0x28,0x7C, /* # */                         
			0x7C,0x44,0x7C, /* 0x */                        
			0x24,0x10,0x48, /* % */                         
			0x28,0x54,0x08, /* & */                         
			0x00,0x0C,0x00, /* ' */                         
			0x38,0x44,0x00, /* ( */                         
			0x44,0x38,0x00, /* ) */                         
			0x20,0x10,0x08, /* // */                        
			0x10,0x38,0x10, /* + */                         
			0x80,0x40,0x00, /* , */                         
			0x10,0x10,0x10, /* - */                         
			0x00,0x40,0x00, /* . */                         
			0x20,0x10,0x08, /* / */    
			0x38,0x44,0x38, /* 0	0x30 */                     
			0x00,0x7C,0x00, /* 1 */                         
			0x64,0x54,0x48, /* 2 */                         
			0x44,0x54,0x28, /* 3 */                         
			0x1C,0x10,0x7C, /* 4 */                         
			0x4C,0x54,0x24, /* 5 */                         
			0x38,0x54,0x20, /* 6 */                         
			0x04,0x74,0x0C, /* 7 */                         
			0x28,0x54,0x28, /* 8 */                         
			0x08,0x54,0x38, /* 9 */                         
			0x00,0x50,0x00, /* : */                         
			0x80,0x50,0x00, /* ; */                         
			0x10,0x28,0x44, /* < */                         
			0x28,0x28,0x28, /* = */                  
			0x44,0x28,0x10, /* > */                        
			0x04,0x54,0x08, /* ? */                         
			0x38,0x4C,0x5C, /* @	0x40 */                          
			0x78,0x14,0x78, /* A */                         
			0x7C,0x54,0x28, /* B */                         
			0x38,0x44,0x44, /* C */                         
			0x7C,0x44,0x38, /* D */                         
			0x7C,0x54,0x44, /* E */                         
			0x7C,0x14,0x04, /* F */                         
			0x38,0x44,0x34, /* G */                         
			0x7C,0x10,0x7C, /* H */                         
			0x00,0x7C,0x00, /* I */                         
			0x20,0x40,0x3C, /* J */                         
			0x7C,0x10,0x6C, /* K */                         
			0x7C,0x40,0x40, /* L */                         
			0x7C,0x08,0x7C, /* M */                         
			0x7C,0x04,0x7C, /* N */                         
			0x7C,0x44,0x7C, /* O */                         
			0x7C,0x14,0x08, /* P	0x50 */                 
			0x38,0x44,0x78, /* Q */                         
			0x7C,0x14,0x68, /* R */                         
			0x48,0x54,0x24, /* S */                         
			0x04,0x7C,0x04, /* T */                         
			0x7C,0x40,0x7C, /* U */                         
			0x3C,0x40,0x3C, /* V */                         
			0x7C,0x20,0x7C, /* W */                         
			0x6C,0x10,0x6C, /* X */                         
			0x1C,0x60,0x1C, /* Y */                         
			0x64,0x54,0x4C, /* Z */                         
			0x7C,0x44,0x00, /* [ */                         
			0x08,0x10,0x20, /* \ */                         
			0x44,0x7C,0x00, /* ] */                         
			0x08,0x04,0x08, /* ^ */                         
			0x80,0x80,0x80, /* _ */                         
			0x04,0x08,0x00, /* `	0x60 */                 
			
			
			0x78,0x14,0x78, /* A */         /// small is same as capital                
			0x7C,0x54,0x28, /* B */                         
			0x38,0x44,0x44, /* C */                         
			0x7C,0x44,0x38, /* D */                         
			0x7C,0x54,0x44, /* E */                         
			0x7C,0x14,0x04, /* F */                         
			0x38,0x44,0x34, /* G */                         
			0x7C,0x10,0x7C, /* H */                         
			0x00,0x7C,0x00, /* I */                         
			0x20,0x40,0x3C, /* J */                         
			0x7C,0x10,0x6C, /* K */                         
			0x7C,0x40,0x40, /* L */                         
			0x7C,0x08,0x7C, /* M */                         
			0x7C,0x04,0x7C, /* N */                         
			0x7C,0x44,0x7C, /* O */                         
			0x7C,0x14,0x08, /* P	0x50 */                 
			0x38,0x44,0x78, /* Q */                         
			0x7C,0x14,0x68, /* R */                         
			0x48,0x54,0x24, /* S */                         
			0x04,0x7C,0x04, /* T */                         
			0x7C,0x40,0x7C, /* U */                         
			0x3C,0x40,0x3C, /* V */                         
			0x7C,0x20,0x7C, /* W */                         
			0x6C,0x10,0x6C, /* X */                         
			0x1C,0x60,0x1C, /* Y */                         
			0x64,0x54,0x4C, /* Z */                         
			0x7C,0x44,0x00, /* [ */                         
			0x08,0x10,0x20, /* \ */                         
			0x44,0x7C,0x00, /* ] */                         
			0x08,0x04,0x08, /* ^ */                         
			0x80,0x80,0x80, /* _ */                         
			0x04,0x08,0x00 /* `	0x60 */                 
		};
        public byte[] vram = new byte[128 * 64 / 8];
		
        public GPainter()
        {
        }

        public void FullRectSE(int sx, int sy, int ex, int ey)
        {
            int i, x;

            for (i = sx; i <= ex; i++)
                for (x = sy; x <= ey; x++)
                    SetPixel(i, x);
        }
		
        public void RectSE(int sx, int sy, int ex, int ey)
        {
            int i;

            // 2 horizontal lines
            for (i = sx; i < ex; i++)
            {
                SetPixel(i, sy);
                SetPixel(i, ey);
            }
            // 2 vertical lines
            for (i = sy; i <= ey; i++)
            {
                SetPixel(sx, i);
                SetPixel(ex, i);
            }
        }
		
        void Paint7HorizontalPixles(int x, int y, byte p)
        {
            int i;

            for (i = 0; i < 7; i++)
                if ((p & (1 << i)) > 0)
                    SetPixel(x, y + i);
                else
                    ClearPixel(x, y + i);//clear
        }
		
        public void PrintSmall(int x, int y, char c)
        {
            int i;

            c -= ' ';

            for (i = 0; i < 3; i++)
                Paint7HorizontalPixles(x + i, y, character_small[c*3+i]);
            Paint7HorizontalPixles(x + i, y, 0);
        }
		
        public void PrintSmall(int x, int y, string str)
        {
            for (int i = 0; i < str.Length; i++)
            {
                PrintSmall(x + i * 4, y, str[i]);
            }
        }
		
        void Paint8HorizontalPixles(int x, int y, byte p)
        {
            int i;

            for (i = 0; i < 8; i++)
                if ((p & (1 << i)) > 0)
                    SetPixel(x, y + i);
                else
                    ClearPixel(x, y + i);//clear
        }
		
        public void Print(int x, int y, string str)
        {
            for (int i = 0; i < str.Length; i++)
            {
                Print(x + i * 6, y, str[i]);
            }
        }
		
        public void Print(int x, int y, char c)
        {
            int i;
            if (c > '~')
                c = ' ';

            for (i = 0; i < 5; i++)
                Paint8HorizontalPixles(x + i, y, gylph_reg[c*5+i]);
            Paint8HorizontalPixles(x + i, y, 0);
        }

        public void Clear()
        {
            Array.Clear(vram, 0, vram.Length);
        }
		
        public void SetPixel(int x, int y)
        {
            if (x >= 128 || y >= 64)
                return;
            int index = x + (y / 8) * 128;
            vram[index] |= (byte)(1 << (y % 8));
        }
		
        public void ClearPixel(int x, int y)
        {
            if (x >= 128 || y >= 64)
                return;
            int index = x + (y / 8) * 128;
            vram[index] &= (byte)(~(1 << (y % 8)));
        }
		
        private void circlePoints(int cx, int cy, int x, int y)
        {
            if (x == 0)
            {
                SetPixel(cx, cy + y);
                SetPixel(cx, cy - y);
                SetPixel(cx + y, cy);
                SetPixel(cx - y, cy);
            }
            else
                if (x == y)
                {
                    SetPixel(cx + x, cy + y);
                    SetPixel(cx - x, cy + y);
                    SetPixel(cx + x, cy - y);
                    SetPixel(cx - x, cy - y);
                }
                else
                    if (x < y)
                    {
                        SetPixel(cx + x, cy + y);
                        SetPixel(cx - x, cy + y);
                        SetPixel(cx + x, cy - y);
                        SetPixel(cx - x, cy - y);
                        SetPixel(cx + y, cy + x);
                        SetPixel(cx - y, cy + x);
                        SetPixel(cx + y, cy - x);
                        SetPixel(cx - y, cy - x);
                    }
        }
		
        public void Line(int x0, int y0, int x1, int y1)
        {
            int dy = y1 - y0;
            int dx = x1 - x0;
            int stepx, stepy;

            if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; }
            if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }
            dy <<= 1;                                                  // dy is now 2*dy
            dx <<= 1;                                                  // dx is now 2*dx

            SetPixel(x0, y0);
            if (dx > dy)
            {
                int fraction = dy - (dx >> 1);                         // same as 2*dy - dx
                while (x0 != x1)
                {
                    if (fraction >= 0)
                    {
                        y0 += stepy;
                        fraction -= dx;                                // same as fraction -= 2*dx
                    }
                    x0 += stepx;
                    fraction += dy;                                    // same as fraction -= 2*dy
                    SetPixel(x0, y0);
                }
            }
            else
            {
                int fraction = dx - (dy >> 1);
                while (y0 != y1)
                {
                    if (fraction >= 0)
                    {
                        x0 += stepx;
                        fraction -= dy;
                    }
                    y0 += stepy;
                    fraction += dx;
                    SetPixel(x0, y0);
                }
            }
        }

        public void Cirlcle(int xCenter, int yCenter, int radius)
        {
            int x = 0;
            int y = radius;
            int p = (5 - radius * 4) / 4;

            circlePoints(xCenter, yCenter, x, y);
            while (x < y)
            {
                x++;
                if (p < 0)
                {
                    p += 2 * x + 1;
                }
                else
                {
                    y--;
                    p += 2 * (x - y) + 1;
                }
                circlePoints(xCenter, yCenter, x, y);
            }
        }
    }
}